package com.twq.abstractclasses

/**
  * 创建一个抽象类
  */
abstract class Element {

  /**
    * 抽象方法 无方法体 无参数可以省略括号‘（）’
    * 输入：无
    * 输出：字符串数组
    * def contents(): Array[String]
    */
  def contents: Array[String]

  /**
    * 具体方法
    * @return 返回contents的长度，return可以省略
    */
  def height(): Int = contents.length

  /**
    * 具体方法 无参数可以省略括号‘（）’
    * @return
    */
  def wedth: Int = if(height == 0) 0 else contents(0).length
  //val wedth: Int = if(height == 0) 0 else contents(0).length
  //使用val 相当于wedth是一个成员变量，当类一加载时，成员变量就会进行计算，
  //使用def 是一个方法，只有方法被调用的时候才会计算，比val更省内存

  def demo() = print("Element")
}

//定义一个子类
//class ArrayElement(conts: Array[String] extends Element {
//  //override 可以省 编译器自动判断是继承来的
//  override def contents(): Array[String] = conts
//}
//上面的简写
class ArrayElement(val contents: Array[String]) extends Element{
  override def demo(): Unit = println("ArrayElement")
}
//只为了单纯复用ArrayElement中的contents
class LineElement(s: String) extends Element {

  override def contents: Array[String] = Array(s)  //组合一个数组

  override def height(): Int = 1
  override def wedth: Int = s.length
  override def demo(): Unit = println("LineElement")
}
//再定义一个子类(功能：指定一个宽度指定一个高度来填充字符)
class UniformElement(ch: Char, override val height: Int, override val wedth: Int) extends Element {
  val line = ch * wedth // 如果ch为‘f’,wedth=3, 那么line="fff"
  //子类需要实现Element中的抽象方法
  override def contents: Array[String] = Array.fill(height)(line.toString) //height=2 --> "fff","fff"
}

//测试
object Element {
  def main(args: Array[String]): Unit = {
    val ele = new ArrayElement(Array("eeee","232fdg"))
    println(ele.height())
    println(ele.wedth)
    val eer = new LineElement("dhdhdh")
    println(eer.height())
    println(eer.wedth)

    //多态 ：声明时类型可以是Element，编译时类型可以是ArrayElement子类或其他子类
    val element1: Element = new ArrayElement(Array("eweewe","dsdsd"))
    val element2: Element = new LineElement("sddsd333")
    val element3: Element = element2
    val element4: Element = new UniformElement('d',1,2)

    //绑定
    invokeDemo(element1)  //输出ArrayElement 他会绑定运行时的类
    invokeDemo(element2)  //输出LineElement
    invokeDemo(element3)  //输出LineElement
    invokeDemo(element4)  //输出Element

  }

  def invokeDemo(element: Element) = {
    element.demo() //调用类中的demo方法
  }

}